🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / ble / src / commonTest / kotlin / org / meshtastic / core / ble / KableBleConnectionTest.kt
Displaying Raw • Download
core/ble/src/commonTest/kotlin/org/meshtastic/core/ble/KableBleConnectionTest.kt 4d4070c8e1ed73a56ee2f054300a0f62e3fe2e62 (4d4070c8) Text, 11.27 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.ble
Tff7b72import T7ee787app.cash.turbine.test
Tff7b72import T7ee787com.juul.kable.Advertisement
Tff7b72import T7ee787dev.mokkery.MockMode
Tff7b72import T7ee787dev.mokkery.mock
Tff7b72import T7ee787kotlinx.coroutines.ExperimentalCoroutinesApi
Tff7b72import T7ee787kotlinx.coroutines.awaitCancellation
Tff7b72import T7ee787kotlinx.coroutines.flow.Flow
Tff7b72import T7ee787kotlinx.coroutines.flow.emptyFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.flow.flow
Tff7b72import T7ee787kotlinx.coroutines.flow.flowOf
Tff7b72import T7ee787kotlinx.coroutines.flow.toList
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787kotlinx.coroutines.test.advanceTimeBy
Tff7b72import T7ee787kotlinx.coroutines.test.advanceUntilIdle
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertFailsWith
Tff7b72import T7ee787kotlin.test.assertIs
Tff7b72import T7ee787kotlin.test.assertSame
Tff7b72import T7ee787kotlin.test.assertTrue
Tff7b72import T7ee787kotlin.time.Duration.Companion.seconds
Tff7b72import T7ee787kotlin.uuid.Uuid
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalCoroutinesApiTff7b72::Te6edf3classTb4b4b4)
Tff7b72class T56d364KableBleConnectionTest Tb4b4b4{
Tf0883e@Test
Tff7b72fun Td2a8ff`scan emits ble device for discovered advertisement`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3advertisementTb4b4b4: Te6edf3Advertisement Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(
Te6edf3scanResults Tff7b72=
Te6edf3flowOfTb4b4b4(
Te6edf3KableScanResultTb4b4b4(
Te6edf3identifier Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:FFTa5d6ff"Tb4b4b4,
Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffMeshtasticTa5d6ff"Tb4b4b4,
Te6edf3advertisement Tff7b72= Te6edf3advertisementTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3result Tff7b72= Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3device Tff7b72= Te6edf3assertIsTff7b72<Te6edf3MeshtasticBleDeviceTff7b72>Tb4b4b4(Te6edf3resultTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:FFTa5d6ff"Tb4b4b4, Te6edf3deviceTb4b4b4.Te6edf3addressTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffMeshtasticTa5d6ff"Tb4b4b4, Te6edf3deviceTb4b4b4.Te6edf3nameTb4b4b4)
Te6edf3assertSameTb4b4b4(Te6edf3advertisementTb4b4b4, Te6edf3deviceTb4b4b4.Te6edf3advertisementTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan reserves one platform start before collecting advertisements`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3reservationEvent Tff7b72= Ta5d6ff"Ta5d6ffscan-start-reservedTa5d6ff"
Tff7b72val Te6edf3collectionEvent Tff7b72= Ta5d6ff"Ta5d6ffadvertisements-collectedTa5d6ff"
Tff7b72val Te6edf3events Tff7b72= Te6edf3mutableListOfTff7b72<Tffa657StringTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(
Te6edf3scanResults Tff7b72= Te6edf3flow Tb4b4b4{ Te6edf3events Tff7b72+Tff7b72= Te6edf3collectionEvent Tb4b4b4}Tb4b4b4,
Te6edf3scanStartLimiter Tff7b72= Te6edf3BleScanStartLimiter Tb4b4b4{ Te6edf3events Tff7b72+Tff7b72= Te6edf3reservationEvent Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3eventsTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3reservationEvent Tb4b4b4}Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3reservationEventTb4b4b4, Te6edf3collectionEventTb4b4b4)Tb4b4b4, Te6edf3eventsTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`expired timeout does not reserve a platform start`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72var Te6edf3reservations Tff7b72= T79c0ff0
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(Te6edf3scanResults Tff7b72= Te6edf3emptyFlowTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3scanStartLimiter Tff7b72= Te6edf3BleScanStartLimiter Tb4b4b4{ Te6edf3reservations Tff7b72+Tff7b72= T79c0ff1 Tb4b4b4}Tb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff0.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff0Tb4b4b4, Te6edf3reservationsTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`timeout terminates scan`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72var Te6edf3cancelled Tff7b72= Tff7b72false
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(
Te6edf3scanResults Tff7b72=
Te6edf3flow Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3awaitCancellationTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3cancelled Tff7b72= Tff7b72true
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3collected Tff7b72= Te6edf3mutableListOfTff7b72<Te6edf3BleDeviceTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3job Tff7b72= Te6edf3backgroundScopeTb4b4b4.Te6edf3launch Tb4b4b4{ Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Te6edf3collectedTb4b4b4) Tb4b4b4}
Te6edf3advanceTimeByTb4b4b4(T79c0ff1.Te6edf3secondsTb4b4b4.Te6edf3inWholeMilliseconds Tff7b72+ T79c0ff1Tb4b4b4)
Te6edf3advanceUntilIdleTb4b4b4(Tb4b4b4)
Te6edf3jobTb4b4b4.Te6edf3joinTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3jobTb4b4b4.Te6edf3isCompletedTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3cancelledTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3collectedTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`service uuid filter is applied`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3serviceUuid Tff7b72= Te6edf3UuidTb4b4b4.Te6edf3parseTb4b4b4(Ta5d6ff"Ta5d6ff12345678-1234-1234-1234-1234567890abTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3TestKableBleScannerTb4b4b4(Te6edf3scanResults Tff7b72= Te6edf3emptyFlowTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4, Te6edf3serviceUuid Tff7b72= Te6edf3serviceUuidTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3KableScanFilterTb4b4b4.Te6edf3ServiceUuidTb4b4b4(Te6edf3serviceUuidTb4b4b4)Tb4b4b4, Te6edf3scannerTb4b4b4.Te6edf3lastFilterTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`address filter is used natively only where the platform supports it`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3address Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:FFTa5d6ff"
Te6edf3assertEqualsTb4b4b4(
Te6edf3KableScanFilterTb4b4b4.Te6edf3AddressTb4b4b4(Te6edf3addressTb4b4b4)Tb4b4b4,
Te6edf3resolveKableScanFilterTb4b4b4(Te6edf3serviceUuid Tff7b72= Tff7b72nullTb4b4b4, Te6edf3address Tff7b72= Te6edf3addressTb4b4b4, Te6edf3supportsAddressFilter Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4,
Tb4b4b4)
T8b949e// Without native support the scan must stay unfiltered rather than carry a filter that matches nothing.
Te6edf3assertEqualsTb4b4b4(
Te6edf3KableScanFilterTb4b4b4.Te6edf3NoneTb4b4b4,
Te6edf3resolveKableScanFilterTb4b4b4(Te6edf3serviceUuid Tff7b72= Tff7b72nullTb4b4b4, Te6edf3address Tff7b72= Te6edf3addressTb4b4b4, Te6edf3supportsAddressFilter Tff7b72= Tff7b72falseTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`service uuid wins over address when the platform cannot filter by address`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3serviceUuid Tff7b72= Te6edf3UuidTb4b4b4.Te6edf3parseTb4b4b4(Ta5d6ff"Ta5d6ff12345678-1234-1234-1234-1234567890abTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3address Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:FFTa5d6ff"
Te6edf3assertEqualsTb4b4b4(
Te6edf3KableScanFilterTb4b4b4.Te6edf3AddressTb4b4b4(Te6edf3addressTb4b4b4)Tb4b4b4,
Te6edf3resolveKableScanFilterTb4b4b4(Te6edf3serviceUuid Tff7b72= Te6edf3serviceUuidTb4b4b4, Te6edf3address Tff7b72= Te6edf3addressTb4b4b4, Te6edf3supportsAddressFilter Tff7b72= Tff7b72trueTb4b4b4)Tb4b4b4,
Tb4b4b4)
T8b949e// Kable's btleplug backend matches address filters against a hardcoded null, so an address filter here would
T8b949e// silently yield no advertisements at all and BLE connect could never find the device.
Te6edf3assertEqualsTb4b4b4(
Te6edf3KableScanFilterTb4b4b4.Te6edf3ServiceUuidTb4b4b4(Te6edf3serviceUuidTb4b4b4)Tb4b4b4,
Te6edf3resolveKableScanFilterTb4b4b4(Te6edf3serviceUuid Tff7b72= Te6edf3serviceUuidTb4b4b4, Te6edf3address Tff7b72= Te6edf3addressTb4b4b4, Te6edf3supportsAddressFilter Tff7b72= Tff7b72falseTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan narrows to the requested address regardless of the native filter`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3wanted Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:FFTa5d6ff"
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(
Te6edf3scanResults Tff7b72=
Te6edf3flowOfTb4b4b4(
Te6edf3KableScanResultTb4b4b4(Te6edf3identifier Tff7b72= Ta5d6ff"Ta5d6ff11:22:33:44:55:66Ta5d6ff"Tb4b4b4, Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffOtherTa5d6ff"Tb4b4b4, Te6edf3advertisement Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Te6edf3KableScanResultTb4b4b4(Te6edf3identifier Tff7b72= Te6edf3wantedTb4b4b4, Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffMeshtasticTa5d6ff"Tb4b4b4, Te6edf3advertisement Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Te6edf3KableScanResultTb4b4b4(Te6edf3identifier Tff7b72= Ta5d6ff"Ta5d6ff77:88:99:AA:BB:CCTa5d6ff"Tb4b4b4, Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffAnotherTa5d6ff"Tb4b4b4, Te6edf3advertisement Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4, Te6edf3address Tff7b72= Te6edf3wantedTb4b4b4)Tb4b4b4.Te6edf3test Tb4b4b4{
Te6edf3assertEqualsTb4b4b4(Te6edf3wantedTb4b4b4, Te6edf3awaitItemTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3addressTb4b4b4)
Te6edf3awaitCompleteTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan matches the requested address case-insensitively`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
T8b949e// A second, non-matching advertisement keeps this honest: a scan that ignored the address entirely would
T8b949e// emit both, so the assertion proves the filter ran *and* that it matched across case.
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(
Te6edf3scanResults Tff7b72=
Te6edf3flowOfTb4b4b4(
Te6edf3KableScanResultTb4b4b4(Te6edf3identifier Tff7b72= Ta5d6ff"Ta5d6ff11:22:33:44:55:66Ta5d6ff"Tb4b4b4, Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffOtherTa5d6ff"Tb4b4b4, Te6edf3advertisement Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Te6edf3KableScanResultTb4b4b4(Te6edf3identifier Tff7b72= Ta5d6ff"Ta5d6ffaa:bb:cc:dd:ee:ffTa5d6ff"Tb4b4b4, Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffMeshtasticTa5d6ff"Tb4b4b4, Te6edf3advertisement Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4, Te6edf3address Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:FFTa5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3test Tb4b4b4{
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffaa:bb:cc:dd:ee:ffTa5d6ff"Tb4b4b4, Te6edf3awaitItemTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3addressTb4b4b4)
Te6edf3awaitCompleteTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan without an address emits every advertisement`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(
Te6edf3scanResults Tff7b72=
Te6edf3flowOfTb4b4b4(
Te6edf3KableScanResultTb4b4b4(Te6edf3identifier Tff7b72= Ta5d6ff"Ta5d6ff11:22:33:44:55:66Ta5d6ff"Tb4b4b4, Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffOneTa5d6ff"Tb4b4b4, Te6edf3advertisement Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Te6edf3KableScanResultTb4b4b4(Te6edf3identifier Tff7b72= Ta5d6ff"Ta5d6ff77:88:99:AA:BB:CCTa5d6ff"Tb4b4b4, Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffTwoTa5d6ff"Tb4b4b4, Te6edf3advertisement Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3test Tb4b4b4{
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ff11:22:33:44:55:66Ta5d6ff"Tb4b4b4, Te6edf3awaitItemTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3addressTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ff77:88:99:AA:BB:CCTa5d6ff"Tb4b4b4, Te6edf3awaitItemTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3addressTb4b4b4)
Te6edf3awaitCompleteTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan wraps Android scanner registration failure`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(
Te6edf3scanResults Tff7b72= Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffFailed to start scan as app cannot be registeredTa5d6ff"Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3BleScanStartExceptionTff7b72> Tb4b4b4{ Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Te6edf3BleScanStartFailureReasonTb4b4b4.Te6edf3ApplicationRegistrationFailedTb4b4b4, Te6edf3failureTb4b4b4.Te6edf3reasonTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan wraps nested scanner registration failure`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3innerCause Tff7b72= Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffFailed to start scan as app cannot be registeredTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(Te6edf3scanResults Tff7b72= Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffOuter scanner failureTa5d6ff"Tb4b4b4, Te6edf3innerCauseTb4b4b4) Tb4b4b4}Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3BleScanStartExceptionTff7b72> Tb4b4b4{ Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Te6edf3BleScanStartFailureReasonTb4b4b4.Te6edf3ApplicationRegistrationFailedTb4b4b4, Te6edf3failureTb4b4b4.Te6edf3reasonTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan wraps Android scanning too frequently failure`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3message Tff7b72= Ta5d6ff"Ta5d6ffFailed. App is scanning too frequentlyTa5d6ff"
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3TestKableBleScannerTb4b4b4(Te6edf3scanResults Tff7b72= Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3IllegalStateExceptionTb4b4b4(Te6edf3messageTb4b4b4) Tb4b4b4}Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3BleScanStartExceptionTff7b72> Tb4b4b4{ Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Te6edf3BleScanStartFailureReasonTb4b4b4.Te6edf3ScanningTooFrequentlyTb4b4b4, Te6edf3failureTb4b4b4.Te6edf3reasonTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan wraps missing scan permission failure`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3message Tff7b72= Ta5d6ff"Ta5d6ffMissing required android.permission.ACCESS_COARSE_LOCATION for scanningTa5d6ff"
Tff7b72val Te6edf3scanner Tff7b72= Te6edf3TestKableBleScannerTb4b4b4(Te6edf3scanResults Tff7b72= Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3IllegalStateExceptionTb4b4b4(Te6edf3messageTb4b4b4) Tb4b4b4}Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3BleScanStartExceptionTff7b72> Tb4b4b4{ Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Te6edf3BleScanStartFailureReasonTb4b4b4.Te6edf3MissingScanPermissionTb4b4b4, Te6edf3failureTb4b4b4.Te6edf3reasonTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`scan preserves unrelated illegal state failure`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3scanner Tff7b72=
Te6edf3TestKableBleScannerTb4b4b4(Te6edf3scanResults Tff7b72= Te6edf3flow Tb4b4b4{ Tff7b72throw Te6edf3IllegalStateExceptionTb4b4b4(Ta5d6ff"Ta5d6ffUnexpected scanner stateTa5d6ff"Tb4b4b4) Tb4b4b4}Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3IllegalStateExceptionTff7b72> Tb4b4b4{ Te6edf3scannerTb4b4b4.Te6edf3scanTb4b4b4(Te6edf3timeout Tff7b72= T79c0ff1.Te6edf3secondsTb4b4b4)Tb4b4b4.Te6edf3toListTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffUnexpected scanner stateTa5d6ff"Tb4b4b4, Te6edf3failureTb4b4b4.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72class T56d364TestKableBleScannerTb4b4b4(
Tff7b72private Tff7b72val Te6edf3scanResultsTb4b4b4: Te6edf3FlowTff7b72<Te6edf3KableScanResultTff7b72>Tb4b4b4,
Tff7b72private Tff7b72val Te6edf3scanStartLimiterTb4b4b4: Te6edf3BleScanStartLimiter Tff7b72= Te6edf3NoOpBleScanStartLimiterTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3KableBleScannerTb4b4b4(Te6edf3BleLoggingConfigTb4b4b4.Te6edf3ReleaseTb4b4b4) Tb4b4b4{
Tff7b72var Te6edf3lastFilterTb4b4b4: Te6edf3KableScanFilter? Tff7b72= Tff7b72null
Te6edf3private Tff7b72set
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffreserveScanStartTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3scanStartLimiterTb4b4b4.Te6edf3reserveStartTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffadvertisementsTb4b4b4(Te6edf3filterTb4b4b4: Te6edf3KableScanFilterTb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3KableScanResultTff7b72> Tb4b4b4{
Te6edf3lastFilter Tff7b72= Te6edf3filter
Tff7b72return Te6edf3scanResults
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.16s